/** @jsxImportSource @emotion/react */
import { jsx, css } from "@emotion/react";
import * as React from "react";
import { Text } from "./Text";
import { buttonReset } from "./Button";
import * as VisuallyHidden from '@radix-ui/react-visually-hidden';
import PropTypes from "prop-types";
import { alpha } from "./Theme/colors";
import { useSpring, animated, SpringConfig } from "@react-spring/web";
import { usePrevious } from "./Hooks/previous";
import { Badge } from "./Badge";
import {ColorMode, useTheme} from "./Theme/Providers";
import { IconWrapper } from "./IconWrapper";
import { IconSizes } from "./Icons/IconTypes";
import { noOp } from "./misc/noop";
import { useMeasure } from "./Hooks/use-measure";
import {OnPressFunction} from "./Hooks/pressable-hook";
import { mergeRefs } from "./Hooks/merge-refs";
import { TouchRefComp} from "./Touchable";
import cx from "classnames";
import scrollIntoView from 'scroll-into-view-if-needed';
import {useDrag} from "@use-gesture/react";
import {safeBind} from "./Hooks/compose-bind";
import {IconX} from "./Icons";
import {Popover, PopoverClose, PopoverContent, PopoverDescription, PopoverRefer} from "./Popover";



const hideScrollbar = css`
  ::-webkit-scrollbar {
    height: 0;
    width: 0;
  }
`;

/**
 * Ideas for improving accessibility:
 *
 * Utilize a 'Tabs' container:
 * This will provide a unique id using context to TabList and TabContent.
 * Both of these components go through children and assign
 * each one a unique id consistent with the index. ('random-tab-1', 'random-tab-content-1')
 */

interface SliderPositions {
  right: number;
  left: number;
  value: number;
}

type VariantTabsType = "default" | "evenly-spaced";

export interface TabsProps {
  /** The currently selected index */
  value: number;
  /** Toggle slider visibiliby */
  slider?: boolean;
  /** Whether tabs should be left-aligned or justified */
  variant?:  VariantTabsType;
  /** Change callback to control which tab is selected */
  onChange: (value: number) => void;
  /** Tab elements */
  children: React.ReactElement<TabProps>[];
  /** spring animation configuration */
  animationConfig?: SpringConfig;
}


/**一排按钮，专门用于TAB序号的切换。一般用于配套Pager xxxPage轮播台的选择控制器。
 * 支持-Y轴线的拉拽弹出全部按钮列表全屏自适应展示。
 * Provide a tabbed interface 多个子页面按钮切换器容器.
 */
export const Tabs = ({
  children,
  variant = "default",
  slider: enableSlider = true,
  value,
  animationConfig = { tension: 190, friction: 15, mass: 0.2 },
  onChange,
  ...other
}:TabsProps) => {
  const theme = useTheme();
  const boundary = React.useRef<HTMLDivElement>(null);
  const tablist = React.useRef<HTMLDivElement>(null);
  const refs = React.useRef<Map<number, HTMLButtonElement | null>>(new Map());
  const dark = theme.colors.mode === "dark";

  // We store the 'value' prop to determine when we should actually
  // animated our slider. ie., only when the index changes.
  const [slider, setSlider] = React.useState<SliderPositions>({
    right: 0,
    left: 0,
    value
  });

  const [showSlider, setShowSlider] = React.useState(false);
  const previousSlider = usePrevious(slider);
  // const scrollRef = React.useRef<ScrollViewHandles>(null);
  const boundSize = useMeasure(boundary);

  //boundary底下的滚动才能显示出来。 target：目标一个按钮。
  React.useEffect(() => {
    const target = refs.current!.get(value);
    if (target) {
      scrollIntoView(target, {
        behavior: 'smooth',
        block: 'end',
        boundary: boundary.current,
      });
    }
  }, [value]);

  // measure our elements
  React.useEffect(() => {
    const target = refs.current!.get(value);
    const container = tablist.current!;

    if (target) {
      const cRect = container.getBoundingClientRect();

      // when container is `display: none`, width === 0.
      // ignore this case
      if (cRect.width === 0) {
        return;
      }

      const tRect = target.getBoundingClientRect();
      const left = tRect.left - cRect.left;
      const right = cRect.right - tRect.right;

      setSlider({
        value,
        left: left + 8,
        right: right + 8
      });

      setShowSlider(true);
    }
  }, [value, ]);     //boundSize加上会死循环！ boundSize每次都会变动的对象；

  const spring = useSpring({
    left: slider.left + "px",
    right: slider.right + "px",
    immediate: previousSlider ? previousSlider.value === slider.value : false,
    config: animationConfig   // default friction is 160: speed up our animation slightly
  });

  // Thanks to Ryan Florence for this code
  // https://github.com/reach/reach-ui/blob/master/packages/tabs/src/index.js
  function onKeyDown(e: React.KeyboardEvent) {
    const enabledIndexes = React.Children.map(
      children as React.ReactElement<TabProps>[],
      (child, index) => {
        return child.props.disabled === true ? null : index;
      }
    ).filter(index => index != null);
    const enabledSelectedIndex = enabledIndexes.indexOf(value);

    switch (e.key) {
      case "ArrowRight": {
        const nextEnabledIndex =
          (enabledSelectedIndex + 1) % enabledIndexes.length;
        const nextIndex = enabledIndexes[nextEnabledIndex];
        if (typeof nextIndex === "number") {
          onChange(nextIndex);
        }
        break;
      }

      case "ArrowLeft": {
        const count = enabledIndexes.length;
        const nextEnabledIndex = (enabledSelectedIndex - 1 + count) % count;
        const nextIndex = enabledIndexes[nextEnabledIndex];
        if (typeof nextIndex === "number") {
          onChange(nextIndex);
        }
        break;
      }

      case "Home": {
        onChange(0);
        break;
      }

      case "End": {
        onChange(React.Children.count(children) - 1);
        break;
      }
    }
  }
    //若太多的Tab,支持弹出的列表形式。
    const [openTablist, setOpenTablist] = React.useState(false);

    const bindDrag= useDrag((state) => {
        const { active, axis, movement: [mx,my], cancel, event, swipe:[swpx,swpy],
                            canceled, type } = state;
        event.stopPropagation();
        const normaltrigs= ( !canceled && (Math.abs(swpy)>0 || Math.abs(my)>5) );
        const trigged= !active && axis==='y' && (normaltrigs );
        if(trigged){
          if(normaltrigs){
            setOpenTablist(true);
          }
          cancel();
        }
        else{
          if(!canceled && axis!=='y') {
            cancel();
          }
        }
      } ,
      {
        swipe: {velocity: 0.1},
        filterTaps: true,
    });

  //底下Children用了两处：拉拽可以弹出的按钮列表框。
  return (
    <Popover open={openTablist} onOpenChange={setOpenTablist}>
      <PopoverRefer className="PopoverTrigger" asChild >
        <div
          className="Tabs"
          ref={boundary}
          css={{
            overflowY: "hidden",
            boxShadow: dark ? `0px 3px 2px rgba(0,0,0,0.15)` : "none",
            touchAction: 'none',
            "@media print": {
              boxShadow: 'unset',
            },
          }}
          {...safeBind( bindDrag(), other)}
        >
          <div   className="Tabs__container"
            css={[
              {
                width: "100%",
                whiteSpace: "nowrap",
                paddingBottom: "20px",
                marginBottom: "-20px",
                overflow: "auto",
              },
              hideScrollbar
            ]}
          >
            <div
              className="Tabs__tablist"
              role="tablist"
              ref={tablist}
              onKeyDown={onKeyDown}
              css={{
                display: variant === "evenly-spaced" ? "flex" : "inline-block",
                position: "relative",
                verticalAlign: "bottom",
                "& button": {
                  flex: variant === "evenly-spaced" ? "1" : undefined
                }
              }}
            >
              {React.Children.map(children, (child, i) => {
                return React.cloneElement(child as React.ReactElement<any>, {
                  isActive: i === value,
                  ref: (el: HTMLButtonElement | null) => {
                    refs.current!.set(i, el);
                  },
                  onParentSelect: () => {
                    onChange(i);
                  }
                });
              })}
              {enableSlider && showSlider && (
                <animated.div
                  style={spring}
                  css={{
                    height: "4px",
                    borderTopRightRadius: "8px",
                    borderTopLeftRadius: "8px",
                    marginLeft: theme.spaces.sm,
                    marginRight: theme.spaces.sm,
                    bottom: 0,
                    position: "absolute",
                    background: dark ? "white" : theme.colors.text.selected
                  }}
                />
              )}
            </div>
          </div>
        </div>
      </PopoverRefer>
      <ColorMode colors={theme.modes.light}>
        <PopoverContent className="Popover" >
          <PopoverDescription
              style={{
                display: "flex",
                flexWrap: 'wrap',
              }}
          >
            {React.Children.map(children, (child, i) => {
              return React.cloneElement(child as React.ReactElement<any>, {
                isActive: i === value,
                onPress: (e) => {
                    onChange(i);
                    setOpenTablist(false);
                }
              });
            })}
          </PopoverDescription>
          <PopoverClose>
            <IconX/>
          </PopoverClose>
        </PopoverContent>
      </ColorMode>
    </Popover>
  );
};


// Tabs.propTypes = {
//   value: PropTypes.number.isRequired,
//   onChange: PropTypes.func,
//   slider: PropTypes.bool,
//   variant: PropTypes.oneOf(["default", "evenly-spaced"] as VariantTabsType[]),
//   children: PropTypes.arrayOf(PropTypes.element),
//   animationConfig: PropTypes.shape({
//     tension: PropTypes.number,
//     friction: PropTypes.number,
//     mass: PropTypes.number
//   })
// };

/**
 * Tab
 */

interface LocalTabProps {
  ref: React.Ref<HTMLButtonElement>;
  onParentSelect: () => void;
  isActive: boolean;
}

export interface TabProps
  extends Partial<LocalTabProps>,
    React.HTMLAttributes<HTMLButtonElement> {
  /** The text content of the tab */
  children: React.ReactNode;
  onPress?: OnPressFunction;
  /** The id of the tab to be shared with TabContent */
  id: string;
  component?: React.ElementType<any>;
  [key: string]: any;
  /** An optional badge */
  badge?: React.ReactNode | string | number;
}

/**
 * A clickable tab item
 * 上级Tabs 将会隐形传递一个ref参数给 Tab的，所以只能上React.forwardRef()
 */

export const Tab =
  ({
      onParentSelect,
      isActive,
      id,
      component: Component = "button",
      badge,
      onPress = noOp,
      children,
      ref,
      ...other
    }: TabProps
  ) => {
    const theme = useTheme();
    const dark = theme.colors.mode === "dark";

    /**
     * Determine text color
     */

    function getTextColor(isDark: boolean | undefined) {
      if (isDark) {
        return isActive ? "rgba(255,255,255,1)" : "rgba(255,255,255,0.65)";
      }

      return isActive ? theme.colors.text.selected : theme.colors.text.muted;
    }

    /**
     * Determine badge colors
     */

    function getBadgeColors(isDark: boolean | undefined) {
      let background = "white";
      let color = "white";

      if (isDark) {
        background = isActive ? "white" : "rgba(255,255,255,0.65)";
        color = isActive
          ? theme.colors.text.selected
          : theme.colors.text.selected;
      } else {
        background = isActive
          ? theme.colors.text.selected
          : theme.colors.scales.gray[6];
        color = isActive ? "white" : theme.colors.text.selected;
      }

      return {
        background,
        color
      };
    }

    const mounted = React.useRef(false);
    //代表ref是按钮区域;  forwarded是开放给上一级的{forwardRef.Ref}
    //Tabs会传递给Tab的forwarded是(el: HTMLButtonElement某元素| null) => {refs.current!.set(i, el);点选定这个tab} 传递ref()实际运行该函数。

    const refnew = React.useRef<any>(null);

    /**
     * Handle focus side-effects
     */

    React.useEffect(() => {
      // don't autofocus if mounting
      if (!mounted.current) {
        mounted.current = true;
        return;
      }

      if (!isActive) {
        return;
      }

      if (refnew.current) {
        refnew.current.focus();
      }
    }, [isActive]);

    const onPressFn = React.useCallback(
      e => {
        onPress(e);
        if (onParentSelect) {
          onParentSelect();
        }
      },
      [onParentSelect, onPress]
    );

    return (
      <TouchRefComp
        className={cx("Tab", {
          "Tab--active": isActive
        })}
        component={Component}
        terminateOnScroll={false}
        onPress={onPressFn}
        delay={0}
        css={[
          buttonReset,
          {
            padding: `10px ${theme.spaces.md}`,
            [theme.mediaQueries.lg]: {
              padding: `10px ${theme.spaces.lg}`
            },
            cursor: "pointer",
            color: getTextColor(dark),
            transition: "background 0.35s ease",
            background: "transparent",
            "& span": {
              transition: "color 0.25s cubic-bezier(0.35,0,0.25,1)"
            },
            "& svg": {
              transition: "stroke 0.35s cubic-bezier(0.35,0,0.25,1)",
              stroke: getTextColor(dark) + " !important"
            },
            ":focus": {
              outline: "none",
              background: dark
                ? alpha(theme.colors.background.tint2, 0.15)
                : theme.colors.background.tint2
            },
            ":focus:not([data-focus-visible-added])": {
              outline: "none",
              background: "transparent"
            },
            "&.Touchable--active": {
              color: dark
                ? "rgba(255,255,255,0.4)"
                : alpha(theme.colors.text.selected, 0.4),
              "& svg": {
                stroke:
                  (dark
                    ? "rgba(255,255,255,0.3)"
                    : alpha(theme.colors.text.selected, 0.4)) + " !important"
              }
            },
            "@media print": {
              transition: 'unset',
            },
          }
        ]}
        ref={mergeRefs(refnew, ref)}
        role="tab"
        id={id + "-tab"}
        aria-controls={id}
        aria-selected={isActive}
        tabIndex={isActive ? 0 : -1}
        {...other}
      >
        {typeof children === "string" ? (
          <Text
            wrap={false}
            component="span"
            variant="subtitle"
            css={{
              fontSize: theme.fontSizes[0],
              fontWeight: 600,
              color: "inherit",
              transition: "color 0.25s cubic-bezier(0.35,0,0.25,1)",
              "@media print": {
                transition: 'unset',
              },
            }}
          >
            {children}
          </Text>
        ) : (
          children
        )}

        {/** Render a badge either from a string, or as a custom element */}
        {badge && (
          <div css={{ display: "inline", marginLeft: theme.spaces.sm }}>
            {typeof badge === "string" || typeof badge === "number" ? (
              <Badge css={getBadgeColors(dark)}>{badge}</Badge>
            ) : (
              badge
            )}
          </div>
        )}
      </TouchRefComp>
    );
  };



/**
 * A TabPanel should be used to wrap tab contents, unless those tabs
 * are anchors.
 */

export interface TabPanelProps extends React.HTMLAttributes<HTMLDivElement> {
  /** The id should correspond to the id given to the associated Tab */
  id: string;
}

export const TabPanel = ({
  id,
  ...other
}:TabPanelProps) => (
  <div
    id={id}
    role="tabpanel"
    tabIndex={0}
    aria-labelledby={id + "-tab"}
    {...other}
  />
);



export interface TabIconProps {
  icon: React.ReactNode;
  label: string;
  size?: IconSizes;
}

export const TabIcon = ({
  icon,
  label,
  size = "lg"
}:TabIconProps) => {
  return (
    <div css={{ display: "flex", justifyContent: "center" }}>
      <IconWrapper size={size}>{icon}</IconWrapper>
      <VisuallyHidden.Root>{label}</VisuallyHidden.Root>
    </div>
  );
};
